package kayao.client.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import android.os.SystemClock;
import kayao.client.data.SharedMemory;
import kayao.client.network.message.*;
import kayao.client.scene.KayaoView;
import kayao.common.KayaoDatagramSocket;
import kayao.common.KayaoValues;

public class NetwriteThread extends Thread {
	public static final Object LOCK = new Object();

	private KayaoView mKayaoView;
	private KayaoDatagramSocket mSendSocket;
	private DatagramPacket packet;
	private boolean mRunning = true;
	private boolean mDead = false;
	private boolean mDebug = false;
	private int mLogoutCounter = 0;
	private int mLogoutCount = 0;

	public NetwriteThread(KayaoView kv){
		this.mKayaoView = kv;
		try{
			mSendSocket = new KayaoDatagramSocket(KayaoValues.CLIENT_SEND_PORT);
		} catch(Exception e){
			// TODO: Handle exceptions
			System.out.println("Error in NetwriteThread constructor: " + e.getMessage());
		}
	}
	
	public boolean terminate(){
		if (mDebug) { System.out.println("##NETWRITE## Trying to close netwrite"); }
		mRunning = false;
		mSendSocket.close();
		if (mSendSocket.isClosed() == false){
			System.out.println("##NETWRITE## (NetwriteThread.terminate) Unable to close socket");
		} else {
			if (mDebug) { System.out.println("##NETWRITE## Socket is now closed"); }
		}
		int i = 0;
		while(!mDead){
			synchronized(LOCK){
				this.interrupt();
				LOCK.notify();
			}
			if (i < 10){
				if (mDebug) { System.out.println("##NETWRITE## end of dead loop"); }
				i++;
			} else {
				break;
			}
		}
		return false;
	}
	@Override
	public void run() {
		if (mDebug) { System.out.println("### NetwriteThread started"); }
		while(mRunning) {
			if (!MessageHandler.gotMessagesInQueue()) {
				synchronized(LOCK) {
					try {
						LOCK.wait();
					} catch (InterruptedException e) {
						if (mDebug) { System.out.println("##NETWRITE## Interrupted from wait"); }
					}
				}
			} else {
				try {
					while (MessageHandler.gotMessagesInQueue()) {
						if (mDebug) { System.out.println("##NETWRITE## Got messages in que"); }
						sendOutgoingMessages();
						treatWaitingMessages();
					}					
				} catch (NullPointerException e){
					if (mDebug) { System.out.println("##NETWRITE## Nullpointer in run"); }
				}
			}
		}
		mDead = true;
	}

	/**
	 * Get the response messages and treat them.
	 * They can be put in the outgoing queue or ignored if they have tried enough
	 */
	private void treatWaitingMessages() {
		try {
			if (MessageHandler.gotMessagesAwaitingReply()) {
				// Message manager need to abort sleep if a new message to send is added
				Message m;
				while((m=MessageHandler.getNextMessageAwaitingReply())!=null) {
					if (mDebug) { System.out.println("##NETWRITE## while in treating messages"); }
					long time=SystemClock.uptimeMillis();
					if(time>m.getTimeout())
					{
						System.out.println("##NETWRITE## Got a timeout at msg: " + m.getMessageId() + ", type: "+m.type() +" " + time + " " + m.getTimeout());
						if(m.getRetriesLeft()>=0) {
							MessageHandler.removeAwatingMessage(m.getMessageId());
							if(isItWorthResendMessage(m))
							{
								MessageHandler.insertOutgoingMessage(m);
							}
							else System.out.println("##NETWRITE## Ignored message for be old in the awaiting response queue");
						}
						else
						{
							MessageHandler.removeAwatingMessage(m.getMessageId());
							//Special cases when we get a timeout**************************************
							//*************************************************************************
							if(m instanceof SubscriptionRequestMessage)
							{
								//Create a who's my king (KingRequest) to send to the server
								// when the actual king doesn't answer
								//System.out.println("Got to send dead client message. "+SharedMemory.getInstance().getClientByIp(m.getTargetAddress()).getId());
								MessageHandler.insertOutgoingMessage(new DeadClientMessage(SharedMemory.getInstance().getClientByIp(m.getTargetAddress()).getId()));
								if (mDebug) { System.out.println("##NETWRITE## NetwriteThread- subreq TIMEOUT king req msg"); }
								MessageHandler.insertOutgoingMessage(new KingRequestMessage(KayaoValues.SERVER_IP));
							}
							else if(m instanceof KingRequestMessage)
							{
								//MessageHandler.insertOutgoingMessage(new KingRequestMessage(KayaoValues.SERVER_IP));
							}
						}
					}
					else {
						try {
							if (mDebug) { System.out.println("##NETWRITE## sleep: " + m.getTimeout() + " - " + time + " = " + (m.getTimeout() - time)); }
							Thread.sleep(m.getTimeout() - time);
						} catch (InterruptedException e) {
							if (mDebug) { System.out.println("##NETWRITE## Interrupted exception: "+e.getMessage()); }
							break;
						}
					}
				}
			}
		} catch (NullPointerException e){
			if (mDebug) { System.out.println("##NETWRITE## Nullpointer in treatWaitingMessages"); }
		}

	}

	private boolean isItWorthResendMessage(Message m) {
		if((m instanceof KingRequestMessage &&	
				m.getMessageId()!=KingRequestMessage.getLastMessageId())
				|| (m instanceof SubscriptionRequestMessage &&	
				m.getMessageId()!=SubscriptionRequestMessage.getLastMessageId()))
			return false;
		else return true;
	}

	/**
	 * It iterates in the outgoing queue sending all of them for priorities
	 */
	private void sendOutgoingMessages() {
		if (mDebug) { System.out.println("##NETWRITE## Start of sendoutgoingmessages"); }
		try {
			while (MessageHandler.gotMessagesToSend()) {
				if (mDebug) { System.out.println("##NETWRITE## Got messages to send"); }
				//get message with highest priority
				Message m = MessageHandler.getMessageOutgoing(Message.PRIORITY_HIGH);
				if (m == null){
					m = MessageHandler.getMessageOutgoing(Message.PRIORITY_MEDIUM);				
				} if (m == null){
					m = MessageHandler.getMessageOutgoing(Message.PRIORITY_LOW);				
				} if (m == null){
					if (mDebug) { System.out.println("##NETWRITE## Message null!!!"); }
				} else {
					synchronized(m) {
						if (mDebug) { System.out.println("##NETWRITE## Synchronized m"); }
						//identify the message for special cases: put in queue of awaiting reply if needed
						int port= KayaoValues.CLIENT_READ_PORT;
						if(m instanceof SubscriberMessage ) {
							;
							//Just send it
						}
						else if(m instanceof StateUpdateMessage ) {
							;
							//Just send it
						}
						else if(m instanceof WelcomeMessage ) {
							//change port and 
							port=KayaoValues.CLIENT_LOGIN_PORT;
							if (mDebug) { System.out.println("##NETWRITE## A Welcome message is going to be sent"); }
	
						}
						else if(m instanceof DeadClientMessage) {
							//change port and 
							port=KayaoValues.SERVER_PORT;
						}
						else if(m instanceof LogOutMessage) {
							//change port a
							if (mLogoutCounter == 0){
								port = KayaoValues.SERVER_PORT;
							} else {
								port = KayaoValues.CLIENT_READ_PORT;								
							}
							if (mDebug) { System.out.println("##NETWRITE## Logout msg to: "+m.getTargetAddress()+":"+port); }
						}
						else if(m instanceof SubscriptionRequestMessage ) {
							MessageHandler.insertAwaitingResponseMessage(m);
							if (mDebug) { System.out.println("##NETWRITE## Sending SubscriptionRequestMessage"); }
						}
						else if(m instanceof KingRequestMessage ) {
							MessageHandler.insertAwaitingResponseMessage(m);
							port=KayaoValues.SERVER_PORT;
						}
						else if(m instanceof TeamCommunication) {
							MessageHandler.insertAwaitingResponseMessage(m);
						}
						//send it
						if (mDebug) { System.out.println("##NETWRITE## Sending to: "+m.getTargetAddress()); }
						m.messageSent();
						sendMessage(m, port);
					}
				}
			}
		} catch (NullPointerException e){
			if (mDebug) { System.out.println("##NETWRITE## Nullpointer in sendOutgoingMessages"); }
		}
		if (mDebug) { System.out.println("##NETWRITE## no messages to send"); }
	}
	
	private void sendMessage(Message message, int port){
		try {			
			packet = new DatagramPacket(message.getData(), message.getData().length, InetAddress.getByName(message.getTargetAddress()), port);
			mSendSocket.send(packet);
			// Save stats of packets
			MessageHandler.statistics(message.getData()[4]);
			if(message instanceof LogOutMessage){
				mLogoutCounter++;
//				if (mDebug) { System.out.println("##NETWRITE## Sent a logout msg to: "+message.getTargetAddress()); }
				System.out.println("##NETWRITE## Sent a logout msg to: "+message.getTargetAddress()+":"+port);
				if (mLogoutCounter == mLogoutCount){
					System.out.println("##NETWRITE## Sent logout msg to the server and "+(mLogoutCount-1)+" clients, shutting down");
					mKayaoView.terminate();
				}
			}
//			 else if(message instanceof SubscriberMessage){
//				byte[] hgh = packet.getData();
				//System.out.println("Printing from NetWriteThread: Size: "+hgh.length+" Data:");
				//for(int g=0; g<hgh.length;g++)
				//	System.out.print(hgh[g]);
				//System.out.println("");
//			}
		} catch (UnknownHostException e) {
			System.out.println("Could not send message: "+e.getMessage());
		} catch (IOException e) {
			System.out.println("Could not send message: "+e.getMessage());
		}
	}

	public void setLogoutCount(int count) {
		this.mLogoutCount = count;
	}
	
	public void decreaseLogoutCounter(){
		this.mLogoutCount--;
	}

}
